#include <avr/io.h>
#include <avr/interrupt.h>

// -- Constant Defines -------------------------------------------------------
#define METRIC_CONVERSION_FACTOR 34 // The speed of sound at STP is 340m/2 ==34cm/ms
//----------------------------------------------------------------------------
unsigned int DISTANCE = 0;
int timer16Started = 0;
int activityFlag = 0;

/*
void init_sonar() {
	
	DDRC &= ~(1<<PORTC7);
	//      enable interrupts on overflow and input capture
	 
	//      enable output on pin4  of port C
    DDRC |= (1<<PORTC6);
    
	
	TIMSK3 |= (1<<TOIE3) | (1<<ICIE3);
	//      set clock source to prescaling by 8 and enable noise canceling
    TCCR3B |= (1<<CS30) | (1<<ICNC3);

	//      set input capture edge to rising edge (positive)
    TCCR3B |= (1<<ICES3);



}
*/

/*
* This function is necessary as the 16 bit timer uses 16 bit registers.
* However, the bus is only 8 bit, thus reading the register requires two reads.
* We want this to be atomic, thus we need to disable interrupts when reading
* the timer counter register.
*/
unsigned int readTimerCounter16() {
	unsigned char sreg;
	unsigned int i;
	sreg = SREG; // Save global interrupt flag
	cli(); // Disable interrupts
	i = ICR3; // Read counter 1
	SREG = sreg; // Restore the interrupt flag
	sei(); // Re-enable interrupts
	return i;
} // end-function

// ...........................................................................
/*
* Returns the state of the level trigger.
* 1 if the trigger is currently set to positive triggering,
* 0 if the trigger is set to negative triggering.
*/

int queryLevelTrigger() {
	return (TCCR3B & _BV(ICES3)) > 0;
} // end-function
// ...........................................................................

/*
* Positive == 1 to enable the positive edge triggering,
* anything else enables negative port triggering.
*/
void setLevelTrigger(int positive) {
	if (positive) TCCR3B |= _BV(ICES3);
	else TCCR3B &= !_BV(ICES3);
} // end-function
// ...........................................................................

/*
* This function inverts the level triggering
* (positive -> negative, negative -> positive).
*/
void flipLevelTrigger() {
	TCCR3B ^= _BV(ICES3);
} // end-function
// ...........................................................................


/*
* The timer interrupt handler. This gets called when a the current level trigger
* occurs.
*/
ISR (TIMER3_CAPT_vect) {
	if (queryLevelTrigger()) {
	// We encountered a positive level event
	PORTD |= _BV(PORTD7); // Red
	flipLevelTrigger();
	} else {
		PORTD |= _BV(PORTD6);
		// We encountered a negative level event
		unsigned int TIME = readTimerCounter16()/200;
		DISTANCE = TIME*METRIC_CONVERSION_FACTOR;
		activityFlag = 1;
		flipLevelTrigger();
	} // end-if
} // end-function
// ...........................................................................

void initTimer() {
	DDRC = 0xf0; //Set the Input Capture for Timer1 to input PORTD4
	TIMSK3 = (_BV(ICIE3)); //Enable ICP1 interrupts

	TCCR3B = (_BV(ICNC3)) | (_BV(ICES3)) | (_BV(CS31)); // Configure the initial
	//timer properties
	sei(); //enable interrupts
	timer16Started = 1; // Flag the timer as started.
} // end-function

/*
* Send a pulse, and resets the timer counter to 0.

*/
void sendPulse() {
	unsigned int i;
	PORTC |= _BV(PORTC6);
	// A quick busy loop to ensure the pulse is at least 10us long
	for(i=0;i<500;i++);
	PORTC &= ~_BV(PORTC6);
	// Reset the timer counter to 0
	TCNT3 = 0;
} // end-function

/*
* This method will query the range finder and will get the range.
* The returned value is in centimetres. -1 is returned if a timeout occurs.
*/
void range() {
	if (!timer16Started) {
		// Setup and start the 16 bit timer, if necessary
		initTimer();
	} // end-if
	// Send the pulse
	sendPulse();
	// Wait for a reply
} // end-function


// 
/*
* This function will send out a ping, wait for the response, and then return it.
* Note that if the closest physical object is out-of-range, this method will
* return the maximum range, and will not return any sort of special flag or error.
* Be aware that you shouldn't call this function in a tight loop - the sonar hardware
* needs at least 10ms between pulses. Thus be sure to use a short wait before calling
* it again.
*/
int getRange() {
	activityFlag = 0;
	DISTANCE = 0;
	range();
	while(!activityFlag);
		return DISTANCE;
} // end-function
